package de.gebauer.xbmc;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

public class AddonXmlMerger {

	public static class ArgsBean {

		@Option(name = "-s", aliases = { "sourceDir" }, required = true)
		private File sourceDirectory;

		@Option(name = "-t", aliases = { "targetFile" }, required = true)
		private File targetFile;

		public File getSourceDirectory() {
			return this.sourceDirectory;
		}

		public File getTargetFile() {
			return this.targetFile;
		}

	}

	public static void main(String[] args) {
		try {

			ArgsBean bean = new ArgsBean();
			new CmdLineParser(bean).parseArgument(args);

			File srcDir = bean.getSourceDirectory();

			List<File> files = getAddonXmls(srcDir);

			new AddonXmlMerger().start(files, bean.getTargetFile());

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (CmdLineException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

	private static List<File> getAddonXmls(File srcDir) {
		if (!srcDir.exists()) {
			throw new RuntimeException("Verzeichnis "
					+ srcDir.getAbsolutePath() + " existiert nicht.");
		}

		List<File> files = new ArrayList<File>();

		final File[] listFiles = srcDir.listFiles();
		if (listFiles == null) {
			throw new RuntimeException("Keine addon.xml-Dateien in "
					+ srcDir.getAbsolutePath() + " gefunden.");
		}
		for (File file : listFiles) {
			if (file.isDirectory()) {
				files.addAll(getAddonXmls(file));
			} else if (file.getName().equals("addon.xml")) {
				files.add(file);
			}
		}

		return files;
	}

	private void start(final List<File> sourceFiles, final File target)
			throws ParserConfigurationException, SAXException,
			TransformerException, IOException {
		if (!target.getParentFile().exists()) {
			target.getParentFile().mkdirs();
		}
		final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

		final DocumentBuilder db = dbf.newDocumentBuilder();

		final Document addonsXml = db.newDocument();
		final Element addonsNode = addonsXml.createElement("addons");
		addonsXml.appendChild(addonsNode);

		for (File file : sourceFiles) {
			final Document addonXml = db.parse(file);
			final Element rootElement = addonXml.getDocumentElement();
			final Node importNode = rootElement.cloneNode(true);
			final Node importNode2 = addonsXml.importNode(importNode, true);
			addonsNode.appendChild(importNode2);
		}

		// Serialisation through Tranform.
		DOMSource domSource = new DOMSource(addonsXml);
		FileWriter writer = null;
		try {
			writer = new FileWriter(target);
			StreamResult streamResult = new StreamResult(writer);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer serializer = tf.newTransformer();
			serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			serializer.setOutputProperty(OutputKeys.INDENT, "yes");
			serializer.transform(domSource, streamResult);
			System.out.println("File addons.xml written to "
					+ target.getAbsolutePath() + ".");
		} catch (TransformerException e) {
			handleException(e, writer);
		} catch (IOException e) {
			handleException(e, writer);
		}

	}

	private <T extends Throwable> void handleException(T e, FileWriter writer)
			throws T {
		if (writer != null) {
			try {
				writer.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		throw e;
	}

}
